#include "utils.cpp"
#include <NTL/ZZ_p.h>
#include <NTL/ZZ.h>


namespace redux
{

	template <typename Ty>
	static void redux_mult(GigInt<Ty> *num, const GigInt<Ty> &p)
	{
			//TODO wrapper per varie redux192 ecc. ecc
			if((p.to_str().compare(utl::p_s)) == 0)
				num->mod192();
			else
			{
				ZZ num_z = GigInt<Ty>::GtoZZ(*num);
				ZZ p_z = GigInt<Ty>::GtoZZ(p);
				ZZ_p::init(p_z);
				ZZ_p num_p = to_ZZ_p(num_z);
				GigInt<Ty> temp(NTL::rep(num_p));
				num->chunks = temp.chunks;
			}
	}


}

using namespace NTL;
using namespace std;

//Class used to implement operation mod p
//IMPORTANT: the p module is internal to the class
//and must be changed explicitly throught construction
//or setP() function
template <typename Ty> 
class GigInt_p 
{
	private:
		GigInt<Ty> num;
		GigInt<Ty> p;

	public:
		GigInt_p() {}

		GigInt_p(const GigInt_p<Ty> &that):
			num(that.num), p(that.p) {}

		GigInt_p(const GigInt<Ty> &num, const GigInt<Ty> &p) :
			 p(p) 
		{ this->num=num; this->p=p; this->normalize();  }

		GigInt_p(string &num, string &p)
		{
			GigInt<Ty> t_num(num);
		  	GigInt<Ty> t_p(p);
			this->p = t_p;
			this->num = t_num;
			this->normalize();
		}

		GigInt_p(GigInt<Ty> &num, string &p)
		{
			GigInt<Ty> t_p(p);
			this->p = t_p;
			this->num = num;
			this->normalize();
		}	
		
		GigInt_p(string &num, GigInt<Ty> p)
		{
			GigInt<Ty> t_num(num);
			this->p = p;
			this->num = t_num;
			this->normalize();
		}

		void setNum(const GigInt<Ty> newNum)
		{
			this->num = newNum;
			this->normalize();
		}	

		void setP(const GigInt<Ty> newP)
		{
			this->p = newP;
			this->normalize();
		}

		void normalize()
		{
			if(this->num >= this->p)
			{
				redux::redux_mult(&(this->num), this->p);
			}
		}
		
		
		GigInt_p operator=(const GigInt_p &that)
		{
			this->num = that.num;
			GigInt<Ty> zero(utl::zero);
			if(this->p == zero)
				this->p = that.p;
			this->normalize();
			return (*this);
		}
		
		GigInt_p operator+(const GigInt_p<Ty> &that) const
		{
			GigInt<Ty> temp = this->num + that.num;
			GigInt_p<Ty> result(temp, this->p);
			result.normalize();
			return result;
		}

		GigInt_p operator+(const GigInt<Ty> &that) const
		{
			GigInt<Ty> temp = this->num + that;
			GigInt_p<Ty> result(temp, this->p);
			result.normalize();
			return result;
		}

		GigInt_p operator-(const GigInt_p<Ty> &that) const
		{
			GigInt<Ty> temp;
			try 
			{
				temp = this->getNum() - that.getNum();
			}
			catch(subtractionException &ex)
			{
				temp = this->getP();
				temp = temp - that.getNum() + this->getNum();
			}
			GigInt_p<Ty> result(temp, this->p);
			//result.normalize();
			return result;
		}

		GigInt_p operator-(const GigInt<Ty> &that) const
		{
			GigInt<Ty>temp;
			try 
			{
				temp = this->num - that;
			}
			catch(subtractionException &ex)
			{
				temp = this->p;
				temp = temp - (that - this->num);
			}
			GigInt_p<Ty> result(temp, this->p);
			result.normalize();
			return result;
		}

		GigInt_p operator*(const GigInt_p<Ty> &that) const
		{
			GigInt<Ty> temp;
			temp = this->num * that.num;
			GigInt_p<Ty> result(utl::zero, this->p);
			result.num = temp;
			result.normalize();
			return result;
		}
		
		GigInt_p operator*(const GigInt<Ty> &that) const
		{
			GigInt<Ty> temp;
			temp = this->num * that;
			GigInt_p<Ty> result(utl::zero, this->p);
			result.num = temp;
			result.normalize();
			return result;
		}

		GigInt_p contrary() const
		{
			GigInt_p<Ty> zero(utl::zero, this->p);
			GigInt_p<Ty> result(zero);
			result = zero - (*this);
			return result;
		}
		
		//TODO eliminate all debug code and make const	
		GigInt_p inv()  
		{
			if(this->num >= this->p)
			{
				cerr << "--abnormal--" << endl;
				this->normalize();
			}
			string uno_s = "1";
			GigInt_p<Ty> uno(uno_s, this->p);
			ZZ p = GigInt<Ty>::GtoZZ(this->p);
			ZZ_p::init(p);
			ZZ num = GigInt<Ty>::GtoZZ(this->num);
			ZZ_p num_p = to_ZZ_p(num);
			ZZ_p inv_p;
			NTL::inv(inv_p, num_p);
			ZZ inv = NTL::rep(inv_p);
			GigInt<Ty> g_inv(inv);
			GigInt_p<Ty> result(g_inv, this->p);
			GigInt_p<Ty> prova(utl::zero, this->p);
			prova = (*this) * result;
			ZZ result_z = GigInt<Ty>::GtoZZ(result.getNum());
			ZZ this_z = GigInt<Ty>::GtoZZ(this->getNum());
			ZZ prova_z = GigInt<Ty>::GtoZZ(prova.getNum());
			//if(prova_z != NTL::rep(inv_p * num_p))
			//{
				
				//cerr << "---------Conversion or Multiplication -------" << endl;
				//cerr << "prova   "; prova.dump();
				//cerr << "result  " << this_z * result_z << endl;
			//}
			if(!(prova == uno))
			{
				//cerr << "this   "; this->dump();
				cerr << "prova       "; prova.dump();
				cerr << "result      "; result.getNum().dump();
				cerr << "inv_p       " << inv_p << endl;
				cerr << "inv         " << inv << endl;
				cerr << "this        "; this->getNum().dump();
			  	cerr << "num_p       " << num_p << endl;	
				cerr << "result      "; result.getNum().dump();
				cerr << "this.size() "<< this->getNum().bitSize() << endl;
				cerr << "result.size " << result.getNum().bitSize() << endl;
				cerr << "ZZ mult     " << NTL::rep(inv_p) * NTL::rep(num_p) << endl;
				GigInt<Ty> gigmult;
				
				gigmult = this->getNum() * result.getNum();
				cerr << "Gig mul     "; gigmult.dump();
				cerr << "correct     " << inv_p * num_p << endl;
				throw subex;
			}
			//result.normalize();
			return result;
		}

		GigInt_p operator/(const GigInt_p<Ty> &that) const
		{
			
			GigInt_p<Ty> inv(that);
			inv = inv.inv();
			GigInt_p<Ty> result;
			result.setP(this->p);
			result.num = this->num;
			result = result * inv;
			result.normalize();
			return result;
		
			//GigInt<Ty> res;
			//res = this->num / that.num;
			//GigInt_p<Ty> result(res, this->getP());
			//result.normalize();
			//return result;


		}

		GigInt_p operator/(const GigInt<Ty> &that) const
		{
			GigInt_p<Ty> temp(that, this->p);
			return ((*this) * (temp.inv()));
		}

		bool operator== (const GigInt_p<Ty> &that) const
		{
			return (this->num == that.num && this->p == that.p);
		}

		bool operator== (const GigInt<Ty> &that) const
		{
			return (this->num == that);
		}

		bool operator!= (const GigInt_p<Ty> &that) const
		{	return !((*this) == that); }

		bool operator!= (const GigInt<Ty> &that) const
		{ return !((*this) == that); }


		GigInt<Ty> getP() const
		{
			GigInt<Ty> p(this->p);
			return p;
		}

		GigInt<Ty> getNum() const
		{
			GigInt<Ty> num(this->num);
			return num;
		}
		
			

		void dump() const
		{
			this->num.dump();
			this->p.dump();
		}

		void dumpDebug() const
		{
			this->num.dumpDebug();
			this->p.dumpDebug();
		}
};
		



		
